#include "filme.h"

std::string Filme::getCountry() const {
        return country;
    }

    std::string Filme::getDirector() const {
        return director;
    }

    char* Filme::getKey() const{
        return (char*)key;
    }

    std::string Filme::getOriginTitle() const {
        return originTitle;
    }


    std::string Filme::getPortTitle() const {
        return portTitle;
    }


    char* Filme::getReleaseYear() const {
        return (char*)releaseYear;
    }
    

    char Filme::getStars() const {
        return stars;
    }

    void Filme::setStars(char stars) {
        this->stars = stars;
    }
    void setKey(Filme &A){//Função friend para alterar a chave primária caso haja remoção
        A.key[0] = '*';
        A.key[1] = '|';
    }
    /*Construtor da classe filme*/
    Filme::Filme(std::string port, std::string orig, std::string dir, std::string ano, std::string pais, char nota){
        int i;
        portTitle = port;
        originTitle = orig;
        director = dir;
        for (i=0; i < 4; i++)
            releaseYear[i] = ano[i];
        releaseYear[4]= '\0';
        country = pais;
        stars = nota;
        for (i=0; i < 3; i++)
            key[i] = dir[i];
        for (i=3; i < 5; i++)
            key[i] = ano[i-1];
        key[5] = '\0';
    
    }
 /*Construtor da classe do índice primário*/
 IndicePrimario::IndicePrimario(char* pkey, int r){
     int i;
     for (i=0; i < 5; i++)
            key[i] = pkey[i];
     key[5]='\0';
     endereco = r;
 }
 char* IndicePrimario::getkey() const{
     return (char*)key;
 }
 int IndicePrimario::getendereco() const{
     return endereco;
 }
 bool IndicePrimario::operator< (const IndicePrimario &A) const{
     if (strcmp(key, A.key) < 0)
         return true;
     return false;
 }
 bool IndicePrimario::operator== (const IndicePrimario &A) const{
     if (strcmp(key, A.key) == 0)
         return true;
     return false;
 }
 /*Construtor da classe do índice secundário*/
 IndiceSecundario::IndiceSecundario(char* pkey, std::string port){
     int i;
     for (i=0; i < 5; i++)
            key[i] = pkey[i];
     key[5]='\0';
     portTitle = port;
     
 }

 std::string IndiceSecundario::getportTitle() const{
     return portTitle;
 }
 char* IndiceSecundario::getkey() const{
     return (char*)key;
 }
 bool IndiceSecundario::operator< (const IndiceSecundario &A) const{
     if (portTitle < A.portTitle)
         return true;
     return false;
 }
 bool IndiceSecundario::operator== (const IndiceSecundario &A) const{
     if (portTitle == A.portTitle)
         return true;
     return false;
 }
 /*Sobrecarga do operador << para a classe do índice primário*/
 std::ostream & operator << (std::ostream &os, const IndicePrimario & p)
{
	os << "chave: " << p.getkey() << ", endereço: " << p.getendereco();
	return os;
 }
 /*Sobrecarga do operador << para a classe do índice secundário*/
  std::ostream & operator << (std::ostream &os, const IndiceSecundario & p)
{
	os << "chave: " << p.getkey() << ", título: " << p.getportTitle();
	return os;
 }
 /*Sobrecarga do operador << para a classe filme*/
 std::ostream & operator << (std::ostream &os, const Filme & p)
{
	os << "chave: " << p.getKey() << std::endl << "Titulo: " << p.getPortTitle() <<
                std::endl << "Tit. Orig.: " << p.getOriginTitle() << std::endl << 
                "Diretor: " << p.getDirector() << std::endl << "ano: " << p.getReleaseYear() <<
                std::endl << "pais: " << p.getCountry() << std::endl << "nota:" << 
                p.getStars() << std::endl;
	return os;
 }
 
 /*Função que verifica se os arquivos de índice e registro existem, 
 senão os cria e coloca uma flag no arquivo.*/
void abreArquivos(){
        FILE *data, *pkey, *skey;
        data = pkey = skey = NULL;
        if((data = fopen("movie.dat", "r")) == NULL)
                data = fopen("movie.dat", "w");
        if((pkey = fopen("iprimary.idx", "r")) == NULL){
            pkey = fopen("iprimary.idx", "w");
            putc('1', pkey);
            putc('\n', pkey);
        }
                
        if((skey = fopen("ititle.idx", "r")) == NULL){
            skey = fopen("ititle.idx", "w");
            putc('1', skey);
            putc('\n', skey);
        }
                
        fclose(data);
        fclose(pkey);
        fclose(skey);
}
/*Função onde o usuário insere os registros de um determinado filme*/
void inserirFilme(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){
    int i = 0;
    Filme* xpto;
    std::string tituloPortugues, tituloOriginal, diretor, pais, ano;
    char nota;
    std::cout << "Digite o nome do filme em português: " << std::endl;
    std::getline(std::cin, tituloPortugues);
    maiuscula(tituloPortugues);
    std::cout << "Digite o nome original do filme: " << std::endl;
    std::getline(std::cin, tituloOriginal);
    maiuscula(tituloOriginal);
    std::cout << "Digite o nome do Diretor do filme: " << std::endl;
    std::getline(std::cin, diretor);
    maiuscula(diretor);
    std::cout << "Digite o ano de lançamento do filme: " << std::endl;
    std::getline(std::cin, ano);
    std::cout << "Digite o país de origem do filme: " << std::endl;
    std::getline(std::cin, pais);
    maiuscula(pais);
    std::cout << "Digite a nota que você dá ao filme: " << std::endl;
    std::cin >> nota;
    i = tituloPortugues.size()+tituloOriginal.size()+diretor.size()+pais.size();
    if (i > 175){
        std::cout << "Você estorou o limite máximo de caracteres em " << i << " por favor, reescreva os titulos" << std::endl;
    }
    else{
        xpto = new Filme(tituloPortugues, tituloOriginal, diretor, ano, pais, nota);
        if(registrarArquivo(xpto, i, iP, iS) == true)
            std::cout << "Registro gravado com sucesso." << std::endl;
        else
            std::cout << "Registro não gravado" << std::endl;
    }
    
        
}
/*Função que dá upper case nas strings*/
void maiuscula(std::string & str){
        int i;
        for (i = 0; i < str.length(); i++)
            str[i] = std::toupper(str[i]);
  }
/*Função que dá upper case nas chars*/
void maiuscula(char* str){
        int i;
        for (i = 0; i < strlen(str); i++)
            str[i] = std::toupper(str[i]);
  }
/*Função que grava no arquivo movie.dat os registros de um determinado filme*/     
bool registrarArquivo( const Filme *xpto, int &i, std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){   
    
    IndicePrimario* iprime;
    IndiceSecundario* isec;
    int j;
    int fileSize=0;
    
    FILE * countfile = fopen ("movie.dat", "a+");
     fseek(countfile, 0, SEEK_END );
     fileSize=ftell (countfile);
     for (j=0; j< 5; j++)
        putc(xpto->getKey()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getPortTitle().size(); j++)
        putc(xpto->getPortTitle()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getOriginTitle().size(); j++)
        putc(xpto->getOriginTitle()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getDirector().size(); j++)
        putc(xpto->getDirector()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< 4; j++)
        putc(xpto->getReleaseYear()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getCountry().size(); j++)
        putc(xpto->getCountry()[j], countfile);
     putc ('@', countfile);
     putc(xpto->getStars(), countfile);
     putc ('@', countfile);
     i = 192-(ftell(countfile)-fileSize);
     for (j=0; j < i; j++)
         putc('#', countfile);      
    fclose (countfile);

  iprime = new IndicePrimario(xpto->getKey(), fileSize );
  isec = new IndiceSecundario(xpto->getKey(), xpto->getPortTitle());
  iP->push_back(*iprime);
  iS->push_back(*isec);
  delete xpto;
  delete iprime;
  delete isec;
  return true;

}
/*Função que copia o índice primário para a memória principal*/
void copiaIndicePrimario(std::vector<IndicePrimario> *iP){
    IndicePrimario* iprime;
    int i, lineLength, rrn;
    std::string buffer, rrnChar;
    char pkey[5];
    size_t pos, ini;
    std::fstream iprimary;
    iprimary.open("iprimary.idx", std::fstream::in);
    if (!iprimary.is_open())
        std::cout << "Erro ao abrir o arquivo" << std::endl;
    else getline(iprimary, buffer);
    std::cout << buffer << std::endl;
    if(buffer!="1"){
        while (!iprimary.eof()){
            getline(iprimary, buffer);
            if(buffer=="")break;
            lineLength = buffer.size();
            strcpy(pkey, buffer.substr(0,5).c_str());
            pos=buffer.find('@');
            pos++;
            ini = pos;
            while (buffer[pos]!='@'){
                pos++;
            }
           rrn = atoi (buffer.substr(ini,pos).c_str());
           iprime = new IndicePrimario(pkey, rrn);
           iP->push_back(*iprime);
           delete iprime;
           
                
        }
    }
    iprimary.close();
    
    
}
/*Função que copia o índice secundário para a memória principal*/
void copiaIndiceSecundario(std::vector<IndiceSecundario> *iS){
    IndiceSecundario* isec;
    int i, lineLength;
    std::string buffer, port;
    char pkey[5];
    size_t pos;
    std::fstream ititle;
    ititle.open("ititle.idx", std::fstream::in);
    if (!ititle.is_open())
        std::cout << "Erro ao abrir o arquivo" << std::endl;
    else getline(ititle, buffer);
    std::cout << buffer << std::endl;
    if(buffer!="1"){
        while (!ititle.eof()){
            getline(ititle, buffer);
            if(buffer=="")break;
            lineLength = buffer.size();
            pos=buffer.find('@');
            port = buffer.substr(0,pos);            
            pos++;
            i=0;
            while (buffer[pos]!='@'){
                pkey[i] = buffer[pos];
                i++;
                pos++;
            }
           isec = new IndiceSecundario(pkey, port);
           iS->push_back(*isec);
           delete isec;
           
                
        }       
    }
    ititle.close();
    
}
/*Salva índice primário no arquivo iprimary.idx*/
void salvarIndice(std::vector<IndicePrimario> *iP){
    int i;
    std::fstream iprimary;
    iprimary.open("iprimary.idx", std::fstream::in | std::fstream::out |std::fstream::trunc);
    if (!iprimary.is_open())
        std::cout << "Erro ao abrir o arquivo" << std::endl;
    else{
        iprimary << '0'<< '\n';
        for(i = 0; i < iP->size(); i++){
            iprimary << iP->at(i).getkey()<<'@'<< iP->at(i).getendereco()<<'@'<< '\n';

        }
        iprimary.close();
    }
    
    
}
/*Salva índice secundário no arquivo ititle.idx*/
void salvaIndiceSec(std::vector<IndiceSecundario> *iS){
    int i, j;
    
    FILE * ititle = fopen ("ititle.idx", "w+");
     fseek(ititle, 0, SEEK_END );
     putc('0', ititle);
     putc('\n', ititle);
     for(i = 0; i < iS->size(); i++){
        for (j=0; j< iS->at(i).getportTitle().size(); j++)
                putc(iS->at(i).getportTitle()[j], ititle);
        putc('@', ititle);
        for (j=0; j < 5; j++)
            putc(iS->at(i).getkey()[j], ititle);
        putc('@', ititle);
        putc('\n', ititle);
     }        
    fclose (ititle);
    
}
/*Função que mostra ao compilador como ordenar o vetor com a classe de índice primário*/
bool ordenaPorChave(IndicePrimario A, IndicePrimario B){
    if (strcmp(A.getkey(), B.getkey())<0)
        return true;
    return false;
}
/*Função que mostra ao compilador como ordenar o vetor com a classe de secundário
 utilizando o título em português*/
bool ordenaPorTitulo(IndiceSecundario A, IndiceSecundario B){
    if (A.getportTitle() < B.getportTitle())
        return true;
    return false;
}
/*Função que define ao compilador como ordenar a na classe o atributo chave primária 
 no índice primário*/
bool verificaChave(IndicePrimario A, const char* busca){
        if(strcmp(A.getkey(), busca)==0)
            return true;
        return false;
}
/*Função que define ao compilador como ordenar a na classe o atributo chave primária 
 no índice secundário*/
bool verificaChave(IndiceSecundario A, const char* busca){
        if(strcmp(A.getkey(), busca)==0)
            return true;
        return false;
}
/*Função que ordena os índices primários e secundário na memória principal*/
void ordenarIndice(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){
    std::sort(iP->begin(), iP->end(), ordenaPorChave);
    std::sort(iS->begin(), iS->end(), ordenaPorTitulo);
    
}
/*Função que busca no índice primário a chave primária */
bool buscaChave(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){
    char busca[5];
    bool achei;
    int j;
    std::vector<IndicePrimario>::iterator i;
    std::cout <<"Digite a chave para fazer a busca"<< std::endl;
    std::cin.getline(busca, 6);
    maiuscula(busca);
    IndicePrimario acha(busca, 0);
    achei = std::binary_search (iP->begin(), iP->end(), acha);
    if(achei){
        i = std::find(iP->begin(), iP->end(), acha);
        std::cout << *i << std::endl;
        carregaRegistro(&(*iP), i->getendereco(), 0);
    } 
    return achei;  
}
/*Função que busca no índice primário a chave primária, dada uma chave de busca e uma flag */
bool buscaChave(std::vector<IndicePrimario> *iP, char* busca, int modifica){
    bool achei;
    int j;
    std::vector<IndicePrimario>::iterator i;
    IndicePrimario acha(busca, 0);
    achei = std::binary_search (iP->begin(), iP->end(), acha);
    if(achei){
        i = std::find(iP->begin(), iP->end(), acha);
        std::cout << *i << std::endl;
        carregaRegistro(&(*iP), i->getendereco(), modifica);
    } 
    return achei;  
}
/*Função que busca no índice secundário o título do filme */
bool buscaTitulo(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){

    std::string busca;
    bool achei;
    char pkey[5];
    std::vector<IndiceSecundario>::iterator i;
    std::cout <<"Digite a chave para fazer a busca"<< std::endl;
    std::getline(std::cin, busca);
    maiuscula(busca);
    IndiceSecundario acha(pkey, busca);
    achei = std::binary_search (iS->begin(), iS->end(), acha);
    if(achei){
        i = std::find(iS->begin(), iS->end(), acha);
        std::cout << *i << std::endl;
        buscaChave(&(*iP), i->getkey(), 0);
    }
    return achei;

}
/*Função que carrega um registro para consulta, alteração ou remoção*/
void carregaRegistro(std::vector<IndicePrimario> *iP,int rrn, int modifica){
    
    int i;
    char filme[192], *pkey, *nota;
    std::string tP, tO, dir, pais, ano;
    FILE * movie = fopen ("movie.dat", "r");
    fseek(movie, rrn, SEEK_SET );
    for (i=0; i<192; i++){
        filme[i] = getc(movie);
    }
     
    filme[192] = '\0';
        pkey = strtok (filme, "@#");
        tP = strtok (NULL, "@#");
        tO = strtok (NULL, "@#");
        dir = strtok (NULL, "@#");
        ano = strtok (NULL, "@#");
        pais = strtok (NULL, "@#");
        nota = strtok (NULL, "@#");
                  
     fclose (movie);
     Filme carrega(tP, tO, dir, ano, pais, *nota);
     std::cout << carrega << std::endl;
     if (modifica == 1){
         char star;
          std::cout << "Digite a nota que você deseja modificar"<<  std::endl;
          std::cin.get(star);
          carrega.setStars(star);
          modificaRegistro(&carrega, rrn);
     }
     if (modifica == 2){
          setKey(carrega);
          modificaRegistro(&carrega, rrn);
     }
}
/*Função que salva os índices primário e secundário da memória principal para os seus 
 respectivos arquivos*/
bool salvarArquivo(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){
    ordenarIndice(&(*iP), &(*iS));
    salvarIndice(&(*iP)); 
    salvaIndiceSec(&(*iS));
    return true;
}
/*Função que lista todos os filmes registrados no arquivo movie.dat*/
void listarFilme(std::vector<IndicePrimario> *iP){
    int i;
    for(i = 0; i < iP->size(); i++){
        carregaRegistro(&(*iP), iP->at(i).getendereco(), 0);
    }
        
}
/*Função que modifica a nota de um dado filme registrado*/
bool mudarNotaFilme(std::vector<IndicePrimario> *iP){
    char pkey[5];
    std::cout<<std::endl;
    std::cout << "Digite a chave do filme" << std::endl;
    std::cin.getline(pkey, 6);
    maiuscula(pkey);
    std::cout<<std::endl;
        
    if(buscaChave(&(*iP), pkey, 1))
        return true;
    return false;
    
}
/*Função de apoio para para as funções mudar nota filme e apagar filme*/
bool modificaRegistro( const Filme *xpto, int &rrn){   
    
    int i,j;
    
    FILE * countfile = fopen ("movie.dat", "r+");
     fseek(countfile, rrn, SEEK_SET );
     for (j=0; j< 5; j++)
        putc(xpto->getKey()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getPortTitle().size(); j++)
        putc(xpto->getPortTitle()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getOriginTitle().size(); j++)
        putc(xpto->getOriginTitle()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getDirector().size(); j++)
        putc(xpto->getDirector()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< 4; j++)
        putc(xpto->getReleaseYear()[j], countfile);
     putc ('@', countfile);
     for (j=0; j< xpto->getCountry().size(); j++)
        putc(xpto->getCountry()[j], countfile);
     putc ('@', countfile);
     putc(xpto->getStars(), countfile);
     putc ('@', countfile);
     i = (rrn+192)-(ftell(countfile));
     for (j=0; j < i; j++)
         putc('#', countfile);      
    fclose (countfile);

  return true;

}
/*Função que remove um dado filme selecionado*/
bool apagarFilme(std::vector<IndicePrimario> *iP, std::vector<IndiceSecundario> *iS){
    char pkey[5];
    std::cout<<std::endl;
    std::cout << "Digite a chave do filme" << std::endl;
    std::cin.getline(pkey, 6);
    maiuscula(pkey);
    std::cout<<std::endl;
        
    if(buscaChave(&(*iP), pkey, 2)){
        ordenarIndice(&(*iP), &(*iS));
        IndicePrimario apaga(pkey, 0);
        std::vector<IndicePrimario>::iterator j;
        j = std::find(iP->begin(), iP->end(), apaga);
        iP->erase(j);
        IndiceSecundario acaba(pkey, "");
        std::vector<IndiceSecundario>::iterator k;
        k = std::find(iS->begin(), iS->end(), acaba);
        iS->erase(k);
    }
        return true;
    return false;
}
